#ifndef BPL_ENTITYMANAGER
#define BPL_ENTITYMANAGER

/* Broadly based on http://www.pastie.org/953838 */

#include <set>
#include <map>
#include <boost/shared_ptr.hpp>
#include "Component.h"
#include "../EntityGrid.h"

namespace BPL
{
class Entity;
typedef unsigned int EntityId;
typedef unsigned int FamilyId;
typedef boost::shared_ptr<Entity> EntityPtr;
typedef boost::shared_ptr<Component> ComponentPtr;

template<typename Key, typename Value, typename Predicate>
void filter(const std::map<Key, Value> &m, std::set<Value> &result, Predicate &p) 
{
   std::map<Key, Value>::const_iterator it = m.begin();
   std::map<Key, Value>::const_iterator end = m.end();

   for( ; it != end; ++it) {
      Key key = it->first;
      if(p(key))
         result.insert(it->second);
   }
}

class EntityManager
{
public:
    EntityManager();
    ~EntityManager();

    EntityId getNextAvailableID();
    void registerEntity(Entity* ent);
    void registerEntity(EntityPtr ent);
	EntityPtr registerNextEntity();
	EntityPtr registerNextStaticEntity(const sf::Vector2i& position);
	EntityPtr registerNextDynamicEntity(const sf::Vector2f& position, int type);
    void registerStaticEntity(EntityPtr ent, const sf::Vector2i& position);
    void registerDynamicEntity(EntityPtr ent, const sf::Vector2f& position, int type);
    void killEntity(EntityId ent);

    EntityPtr getEntity(EntityId ent);
	EntityId getStaticEntityIn(const sf::Vector2i position);
	bool hasEntity(EntityId ent);
    std::map<EntityId, EntityPtr> getAllEntities() const;
    template<typename T> void getEntities(std::set<Entity*> &result);

    template<typename T> boost::shared_ptr<T> getComponent(EntityId id);
    template<typename T> void addComponent(EntityId id, T* comp);
    template<typename T> void addComponent(EntityId id, boost::shared_ptr<T> comp);

	EntityGrid* getGrid() const;
    EntityMap* getMap() const;

	int sizeTile;

protected:
    std::map<EntityId, EntityPtr> mpt_entities;
    std::map<FamilyId, std::map<EntityId, boost::shared_ptr<Component> > > mpt_componentStore;

    EntityGrid* mpt_grid;
    EntityMap* mpt_map;
};


template<typename T> boost::shared_ptr<T> EntityManager::getComponent(EntityId id)
{
    std::map<EntityId, boost::shared_ptr<Component> > &store = mpt_componentStore[T::familyId];
    return boost::dynamic_pointer_cast<T>(store[id]);
}

template<typename T> void EntityManager::getEntities(std::set<Entity*> &result)
{
	struct isEqual 
	{
         isEqual(const FamilyId family_) : family(family_) {}
         bool operator()(const FamilyId val) {
            return val == family;
         }
         FamilyId family;
    };

    filter(mEntities, result, isEqual(T::familyId));
}

template<typename T> void EntityManager::addComponent(EntityId id, T* comp)
{
    mpt_componentStore[T::familyId].insert(std::pair<EntityId, ComponentPtr>(id, ComponentPtr(comp)));
}

template<typename T> void EntityManager::addComponent(EntityId id, boost::shared_ptr<T> comp)
{
    mpt_componentStore[T::familyId].insert(std::pair<EntityId, ComponentPtr>(id, boost::dynamic_pointer_cast<Component>(comp)));
}

}

#endif

